//
// Created by yasin on 2024/9/9.
//

#include "web_socket_client.h"
#include "rtc_log.h"

namespace ohosrtc {

WebSocketClient::WebSocketClient(std::shared_ptr<rtc::Thread> worker_thread) : worker_thread_(std::move(worker_thread)){
}

void WebSocketClient::Connect(const std::string &ws_url,
                              SuccessCallback<> success_cb,
                              ohosrtc::FailCallback fail_cb) {
  if (!worker_thread_->IsCurrent()) {
    worker_thread_->PostTask([=]() {
      Connect(ws_url, success_cb, fail_cb);
    });
    return;
  }
  
  ws_client_ = std::make_unique<WSClient>();
  try {
    // We expect there to be a lot of errors, so suppress them
    ws_client_->clear_access_channels(websocketpp::log::alevel::all);
    ws_client_->clear_error_channels(websocketpp::log::elevel::all);

    // Initialize ASIO
    ws_client_->init_asio();

    // Register our handlers
    ws_client_->set_open_handler([this, success_cb, fail_cb](websocketpp::connection_hdl hdl) {
      RTC_LOG << "websocket connected";
      std::lock_guard<std::mutex> lock(connection_mutex_);
      ws_connection_ = hdl;
      if (success_cb) {
        success_cb();
      }
    });
    
    ws_client_->set_close_handler([this](websocketpp::connection_hdl hdl) {
        RTC_LOG << "websocket connection closed";
        ws_connection_.reset();
    });
    
    ws_client_->set_fail_handler([fail_cb](websocketpp::connection_hdl hdl) {
      RTC_LOG << "websocket connect failed";
      if (fail_cb) {
        fail_cb("websocket connect failed");
      }
    });

    ws_client_->set_message_handler([this](websocketpp::connection_hdl hdl, WSMessagePtr msg) {
      worker_thread_->PostTask([this, msg]() {
        RTC_LOG << "recv websocket message: " << msg->get_payload();
        if (message_handler_) {
          message_handler_(msg->get_payload());
        }  
      });
    });

    websocketpp::lib::error_code ec;
    auto con = ws_client_->get_connection(ws_url, ec);
    con->append_header("Origin", "https://www.google.com");
    ws_client_->connect(con);

    // Start the ASIO io_service run loop
    ws_run_thread_ = std::make_unique<std::thread>(&WSClient ::run, ws_client_.get());
    RTC_LOG << "websocket start done";

  } catch (websocketpp::exception const & e) {
    RTC_LOG << "websocket error: " <<  e.what();
    if (fail_cb) {
      fail_cb(e.what());
    }
  }
}

void WebSocketClient::SendMessage(const std::string &message,
                                  SuccessCallback<> success_cb,
                                  ohosrtc::FailCallback fail_cb) {
  if (!worker_thread_->IsCurrent()) {
    worker_thread_->PostTask([=]() {
      SendMessage(message, success_cb, fail_cb);
    });
    return;
  }
  
  RTC_LOG << "send ws message: " << message;
  
  std::lock_guard<std::mutex> lock(connection_mutex_);
  if (!ws_connection_.lock()) {
    fail_cb("no connection");
    return;
  }
  
  std::error_code code;
  ws_client_->send(ws_connection_, message, websocketpp::frame::opcode::text, code);
  if (code.value() == 0) {
    RTC_LOG << "send ws message success";
    if (success_cb) {
      success_cb();
    }
  } else {
    RTC_LOG << "send ws message failed: " << code.value() << ", " << code.message();
    if (fail_cb) {
      fail_cb(code.message());
    }
  }
}

void WebSocketClient::SetMessageHandler(ohosrtc::WebSocketMessageHandler handler) {
  if (worker_thread_->IsCurrent()) {
    message_handler_ = handler;
  } else {
    worker_thread_->PostTask([this, handler]() {
      message_handler_ = handler;
    });
  }
}

void WebSocketClient::Disconnect() {
  if (!worker_thread_->IsCurrent()) {
    worker_thread_->PostTask([share_this = shared_from_this(), this]() {
      Disconnect();
    });
    return;
  }
  RTC_LOG << "WebSocketClient::Disconnect";
  std::lock_guard<std::mutex> lock(connection_mutex_);
  if (ws_connection_.lock()) {
    try {
      ws_client_->close(ws_connection_, websocketpp::close::status::going_away, "disconnect signaling");
    } catch (websocketpp::exception exception) {
      RTC_LOG << "close connection with error " << exception.what();
    }
  }
}


}